//************************************************************
//************ INCLUDES & DEFINES ****************************
//************************************************************
#include "stdafx.h"
#include "main.h"
#include <time.h>
#include <vld.h>
#include <stdio.h>
#include <io.h>
#include <fcntl.h>
#include "../Game/Game.h"
#include "..\..\EdgeControls\Input.h"
#include "..\..\EdgeGameCore\SettingsManager.h"
#include "../../EdgeUtil/UtilIncludes.h"

using namespace std;

#define MAX_VERTEX_COUNT 100

//************************************************************
//************ SIMPLE WINDOWS APP CLASS **********************
//************************************************************
//************************************************************
//************ CREATION OF OBJECTS & RESOURCES ***************
//************************************************************

/*static*/ DEMO_APP* DEMO_APP::instance = nullptr;

void writeVector(Vector4& v)
{
	printf((to_string(v.x) + ", " + to_string(v.y) + ", " + to_string(v.z) + ", " + to_string(v.w) + '\n').c_str());
}

void writeMatrix(Matrix& m)
{
	writeVector(m.m1);
	writeVector(m.m2);
	writeVector(m.m3);
	writeVector(m.m4);
}

DEMO_APP::DEMO_APP(HINSTANCE hinst, WNDPROC proc, LPWSTR exepath)
{
	// Set up window
	application = hinst;
	appWndProc = proc;

	instance = this;

	WNDCLASSEX  wndClass;
	ZeroMemory(&wndClass, sizeof(wndClass));
	wndClass.cbSize = sizeof(WNDCLASSEX);
	wndClass.lpfnWndProc = appWndProc;
	wndClass.lpszClassName = L"EdgeGame";
	wndClass.hInstance = application;
	wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOWFRAME);
	RegisterClassEx(&wndClass);


	RECT window_size = { 0, 0, SettingsManager::GetInstance()->getSetting("ScreenWidth")._int, SettingsManager::GetInstance()->getSetting("ScreenHeight")._int };
	AdjustWindowRect(&window_size, WS_OVERLAPPEDWINDOW, false);
	
	window = CreateWindow(L"EdgeGame", L"Edge", WS_OVERLAPPEDWINDOW | WS_THICKFRAME,
		CW_USEDEFAULT, CW_USEDEFAULT, window_size.right - window_size.left, window_size.bottom - window_size.top,
		NULL, NULL, application, this);

	Input::Init(window);
	GraphicsManager::Inilitizie(window, window_size.right - window_size.left, window_size.bottom - window_size.top);

	int path_length = lstrlenW(exepath) + 1;
	char* ascii_exepath = new char[path_length];
	for (unsigned int i = 0; i < path_length; i++)
		ascii_exepath[i] = (char)exepath[i];
	Game::instance = new Game(window, ascii_exepath);
	delete ascii_exepath;
	ShowWindow(window, SW_SHOW);
	
	ZeroMemory(&cursor_prev, sizeof(POINT));

	printf("Testing matrix math....\n");

	printf("Matrix invert matrix...\n");

	Matrix inv1 = Matrix(1, 0, 0, 0, 3, 1, 2, 1, 0, 3, 1, 2, 2, 5, 2, 1);
	Matrix correct_inv = Matrix(1.f, 0.f, 0.f, 0.f, 0.25f, -0.25f, 0.f, 0.25f, -1.9166666f, 0.5833333f, -0.33333333f, 0.8333333f, 0.58333333f, 0.833333f, 0.6666666666f, -0.41666666f);
	writeMatrix(inv1);
	printf("\n\n\n");
	printf("Result matrix:\n");
	Matrix inv1_result = Matricies::Invert(inv1);
	writeMatrix(inv1_result);
	printf("\n\n\nCorrect Matrix\n");
	writeMatrix(correct_inv);
}


//************************************************************
//************ EXECUTION *************************************
//************************************************************

float rot = 0.f;


bool DEMO_APP::Run()
{
	static bool first = true;
	if (first)
	{
		then = GetTickCount();
		first = false;
	}
	// determine the amount of elapsed time
	unsigned long now = GetTickCount();
	float dt = (now - then) / 1000.f;
	frames++;
	elapsed += long double(dt);
	if (elapsed > 1.0)
	{
		SetWindowText(window, (L"Edge FPS:" + to_wstring(frames)).c_str());
		elapsed = 0;
		frames = 0;
	}
	Game* pGame = Game::GetInstance();
	// update everything
	pGame->Update(dt);
	// draw everything... no seriously, fucking
	// everything is being renderd here!
	pGame->Draw();

	then = now;

	if (!pGame->running)
		return false;

	return true;
}

//************************************************************
//************ DESTRUCTION ***********************************
//************************************************************

bool DEMO_APP::ShutDown()
{
	UnregisterClass(L"EdgeGame", application);
	Game::GetInstance()->Release();
	return true;
}
//************************************************************
//************ WINDOWS RELATED *******************************
//************************************************************

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wparam, LPARAM lparam);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, LPTSTR args, int argc)
{
	// inilitize memory manager
	u32 args1_size = lstrlenW(&args[1]);
	char args_in[100];
	wchar_t* source = &args[0];
	mbstate_t mbstate;
	wcsrtombs(args_in, (const wchar_t**)&source, argc, &mbstate);
	int heapSize = atoi(args_in);
	EdgeUtil::MemoryManager::GetInstance()->Init(heapSize);
#if _DEBUG
	AllocConsole();

	HANDLE handle_out = GetStdHandle(STD_OUTPUT_HANDLE);
	int hCrt = _open_osfhandle((long)handle_out, _O_TEXT);
	FILE* hf_out = _fdopen(hCrt, "w");
	setvbuf(hf_out, NULL, _IONBF, 1);
	*stdout = *hf_out;

	HANDLE handle_in = GetStdHandle(STD_INPUT_HANDLE);
	hCrt = _open_osfhandle((long)handle_in, _O_TEXT);
	FILE* hf_in = _fdopen(hCrt, "r");
	setvbuf(hf_in, NULL, _IONBF, 128);
	*stdin = *hf_in;

#endif

	srand(unsigned int(time(0)));
	LPWSTR filename = new wchar_t[500];
	memset(filename, 0, 1000);
	GetCurrentDirectory(500, filename);
	
	CoInitializeEx(nullptr, COINIT_MULTITHREADED);
	DEMO_APP myApp(hInstance, (WNDPROC)WndProc, filename);
	delete [] filename;
	
	MSG msg; ZeroMemory(&msg, sizeof(msg));
	while (msg.message != WM_QUIT && myApp.Run())
	{
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}
	myApp.ShutDown();

	CoUninitialize();
	return 0;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	Input::GetInstance()->WinProc(message, wParam, lParam);
	switch (message)
	{
	case (WM_DESTROY) : { PostQuitMessage(0); }
		break;
	case WM_MOUSEMOVE:
		break;
	case WM_ACTIVATE:
		if ((wParam & 0xff) == 0)
			Game::GetInstance()->setWindowFocoused(false);
		else
			Game::GetInstance()->setWindowFocoused(true);
		break;
	}
	return DefWindowProc(hWnd, message, wParam, lParam);
}